package com.order.api.interceptor;

import com.alibaba.fastjson.JSONObject;

import com.order.api.constant.ResultValueEnum;
import com.order.api.po.TblLog;
import com.order.api.service.LogService;
import com.order.api.target.MethodLog;
import com.order.api.vo.BaseResult;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;


/**
 * MethodLog拦截处理类
 */
@Aspect
@Component
@ControllerAdvice
public class SystemLogAspect implements ResponseBodyAdvice<BaseResult>{

    /**
     * 操作日志记录ID
     */
    private Long tblLogId;

    private static String optType = "optType";

    private static String systemModel = "systemModel";

    private static String success = "SUCCESS";

    private static String fail = "FAIL";

    @Autowired
    private LogService logService;

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     * @param joinPoint 切点
     */
    private static Map<String, String> getControllerMethodDescription(JoinPoint joinPoint) {
        Map<String, String> map = new HashMap<String, String>();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        MethodLog controllerLog = method.getAnnotation(MethodLog.class);
        map.put(optType, controllerLog.optType());
        map.put(systemModel, controllerLog.systemModel());
        return map;
    }

    /**
     * Controller层切点 注解拦截
     */
    @Pointcut("@annotation(com.order.api.target.MethodLog)")
    public void controllerAspect() {

    }

    /**
     * 用于拦截Controller层记录用户的操作的开始时间
     * 前置通知
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        Object[] objects = joinPoint.getArgs();
        Map<String, String> rMap = getControllerMethodDescription(joinPoint);
        String requestParam = JSONObject.toJSONString(objects);
        String authentication = request.getHeader("authorization");
        String userId = "";
        if (authentication != null && authentication.length() > 0) {
            String[] param = authentication.split("_");
            if (param.length > 0) {
                userId = param[0];
            }
        }
        Date time = Calendar.getInstance().getTime();
        TblLog tblLog = TblLog.builder()
                .systemModule(rMap.get(systemModel))
                .type(rMap.get(optType))
                .status(success)
                .date(time)
                .requestUrl(request.getRequestURI())
                .requestMethod(request.getMethod())
                .requestParam(requestParam)
                .createId("".equals(userId) || !StringUtils.isNumeric(userId) ? -1 : Long.parseLong(userId))
                .createTime(time)
                .build();
        logService.save(tblLog);
        tblLogId = tblLog.getId();
    }

    /**
     * 用于拦截Controller层记录用户的操作
     * 后置通知
     * @param joinPoint 切点
     */
    @After("controllerAspect()")
    public void doAfter(JoinPoint joinPoint) {

    }

    /**
     * 切面异常处理
     */
    @AfterThrowing(pointcut = "controllerAspect()", throwing = "e")
    public void doException(JoinPoint joinPoint, Throwable e) {
        TblLog tblLog = TblLog.builder()
                .id(tblLogId)
                .status(fail)
                .updateTime(Calendar.getInstance().getTime())
                .build();
        logService.update(tblLog);
    }

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        Method method = methodParameter.getMethod();
        return method.isAnnotationPresent(MethodLog.class);
    }

    @Override
    public BaseResult beforeBodyWrite(BaseResult baseResult, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        TblLog tblLog = TblLog.builder()
                .id(tblLogId)
                .status(ResultValueEnum.SYS_OK.getKey() == baseResult.getCode() ? success : fail)
                .updateTime(Calendar.getInstance().getTime())
                .build();
        logService.update(tblLog);
        return baseResult;
    }
}